package com.ruoyi.common.utils;

import cn.hutool.core.convert.Convert;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;

import java.io.IOException;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;

/**
 * jackson二次封装工具类
 *
 * @author TsingYu
 * @version 1.0
 * @date 2024/11/12 15:52
 */
@SuppressWarnings("unused")
public class JacksonUtils {
    public static final ObjectMapper MAPPER = new ObjectMapper();

    static {
        MAPPER.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        MAPPER.configure(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN, true);
        MAPPER.registerModule(new JavaTimeModule());
    }

    /**
     * 将对象转换为JSON字符串
     *
     * @param object 待转换对象
     * @return JSON字符串
     * @throws JsonProcessingException 如果底层输入包含{@link JsonParser}支持类型（默认情况下为 JSON）的无效内容，抛出此异常
     */
    public static String objectToJson(Object object) throws JsonProcessingException {
        return MAPPER.writeValueAsString(object);
    }

    /**
     * 将JSON字符串转换为对象
     *
     * @param json  JSON字符串
     * @param clazz 对象类型
     * @return Java对象
     * @throws JsonProcessingException 如果底层输入包含{@link JsonParser}支持类型（默认情况下为 JSON）的无效内容，抛出此异常
     */
    public static <T> T jsonToObject(String json, Class<T> clazz) throws JsonProcessingException {
        return MAPPER.readValue(json, clazz);
    }


    /**
     * 从JSON字符串中读取属性值
     *
     * @param json         JSON字符串
     * @param propertyName 属性名
     * @param clazz        属性值类型
     * @return 属性值
     * @throws IOException 如果存在底层I/O问题或格式层解码问题，抛出此异常
     */
    public static <T> T readValueFromJson(String json, String propertyName, Class<T> clazz) throws IOException {
        return MAPPER.readTree(json).get(propertyName).traverse(MAPPER).readValueAs(clazz);
    }

    /**
     * 从JSON字符串转换成<code>List</code>，输入无效JSON字符串或<code>null</code>返回<code>null</code>
     *
     * @param json  JSON字符串
     * @param clazz 返回的List元素类型
     * @return List
     */
    public static <T> List<T> jsonToList(String json, Class<T> clazz) {
        try {
            return MAPPER.readValue(json, new TypeReference<List<T>>() {
                @Override
                public Type getType() {
                    return MAPPER.getTypeFactory().constructCollectionType(List.class, clazz);
                }
            });
        } catch (Exception e) {
            return null;
        }

    }

    /**
     * 判断字符串是否是标准JSON字符串
     * 等同于 !isNotJsonString(String)
     *
     * @param json JSON字符串
     * @return 是JSON字符串返回<code>true</code>，不是JSON字符串返回<code>false</code>
     */
    public static boolean isJsonString(String json) {
        return !isNotJsonString(json);
    }

    /**
     * 判断字符串是否是标准JSON字符串
     *
     * @param json JSON字符串
     * @return 不是JSON字符串返回<code>true</code>，是JSON字符串返回<code>false</code>
     */
    public static boolean isNotJsonString(String json) {
        try {
            MAPPER.readTree(json);
        } catch (Exception e) {
            return true;
        }
        return false;
    }

    /**
     * 从JSON字符串获取属性并返回<code>Object</code>类型
     *
     * @param json         JSON字符串
     * @param propertyName 属性名称
     * @return Object类型的属性值
     * @throws JsonProcessingException 如果底层输入包含{@link JsonParser}支持类型（默认情况下为 JSON）的无效内容，抛出此异常
     */
    public static Object getObject(String json, String propertyName) throws JsonProcessingException {
        if (isNotJsonString(json)) {
            return null;
        }
        JsonNode node = MAPPER.readTree(json).get(propertyName);
        return node == null || node.isNull() ? null : MAPPER.treeToValue(node, Object.class);
    }

    /**
     * 从JSON字符串获取属性并返回<code>String</code>类型
     *
     * @param json         JSON字符串
     * @param propertyName 属性名称
     * @return String类型的属性值
     * @throws JsonProcessingException 如果底层输入包含{@link JsonParser}支持类型（默认情况下为 JSON）的无效内容，抛出此异常
     */
    public static String getString(String json, String propertyName) throws JsonProcessingException {
        if (isNotJsonString(json)) {
            return null;
        }
        JsonNode node = MAPPER.readTree(json).get(propertyName);
        if (node == null || node.isNull()) {
            return null;
        }
        if (node.isTextual()) {
            return node.asText();
        }
        return node.toString();
    }


    /**
     * 从JSON字符串获取属性并返回<code>Integer</code>类型，无法转成<code>Integer</code>则返回<code>null</code>
     *
     * @param json         JSON字符串
     * @param propertyName 属性名称
     * @return Integer类型的属性值
     * @throws JsonProcessingException 如果底层输入包含{@link JsonParser}支持类型（默认情况下为 JSON）的无效内容，抛出此异常
     */
    public static Integer getInteger(String json, String propertyName) throws JsonProcessingException {
        return Convert.toInt(getObject(json, propertyName));
    }


    /**
     * 从JSON字符串获取属性并返回<code>Long</code>类型，无法转成<code>Long</code>则返回<code>null</code>
     *
     * @param json         JSON字符串
     * @param propertyName 属性名称
     * @return Long类型的属性值
     * @throws JsonProcessingException 如果底层输入包含{@link JsonParser}支持类型（默认情况下为 JSON）的无效内容，抛出此异常
     */
    public static Long getLong(String json, String propertyName) throws JsonProcessingException {
        return Convert.toLong(getObject(json, propertyName));
    }

    /**
     * 从JSON字符串获取属性并返回<code>Double</code>类型，无法转成<code>Double</code>则返回<code>null</code>
     *
     * @param json         JSON字符串
     * @param propertyName 属性名称
     * @return Double类型的属性值
     * @throws JsonProcessingException 如果底层输入包含{@link JsonParser}支持类型（默认情况下为 JSON）的无效内容，抛出此异常
     */
    public static Double getDouble(String json, String propertyName) throws JsonProcessingException {
        return Convert.toDouble(getObject(json, propertyName));
    }

    /**
     * 从JSON字符串获取属性并返回<code>Float</code>类型，无法转成<code>Float</code>则返回<code>null</code>
     *
     * @param json         JSON字符串
     * @param propertyName 属性名称
     * @return Float类型的属性值
     * @throws JsonProcessingException 如果底层输入包含{@link JsonParser}支持类型（默认情况下为 JSON）的无效内容，抛出此异常
     */
    public static Float getFloat(String json, String propertyName) throws JsonProcessingException {
        return Convert.toFloat(getObject(json, propertyName));
    }

    /**
     * 从JSON字符串获取属性并返回<code>Boolean</code>类型<br>
     * <p>
     * String支持的值为：true、false、yes、ok、no、1、0 如果给定的值为空，或者转换失败，返回<code>null</code><br>
     *
     * @param json         JSON字符串
     * @param propertyName 属性名称
     * @return Boolean类型的属性值
     * @throws JsonProcessingException 如果底层输入包含{@link JsonParser}支持类型（默认情况下为 JSON）的无效内容，抛出此异常
     */
    public static Boolean getBoolean(String json, String propertyName) throws JsonProcessingException {
        return Convert.toBool(getObject(json, propertyName));
    }

    /**
     * 从JSON字符串获取属性并返回<code>BigDecimal</code>类型，无法转成<code>BigDecimal</code>则返回<code>null</code>
     *
     * @param json         JSON字符串
     * @param propertyName 属性名称
     * @return BigDecimal类型的属性值
     * @throws JsonProcessingException 如果底层输入包含{@link JsonParser}支持类型（默认情况下为 JSON）的无效内容，抛出此异常
     */
    public static BigDecimal getBigDecimal(String json, String propertyName) throws JsonProcessingException {
        return Convert.toBigDecimal(getObject(json, propertyName));
    }

    /**
     * 从JSON字符串获取属性并返回<code>List</code>类型，无法转成<code>List</code>则抛出异常
     *
     * @param json         JSON字符串
     * @param propertyName 属性名称
     * @param clazz        返回的List元素类型
     * @return List类型的属性值
     * @throws JsonProcessingException 如果底层输入包含{@link JsonParser}支持类型（默认情况下为 JSON）的无效内容，抛出此异常
     */
    public static <T> List<T> getList(String json, String propertyName, Class<T> clazz) throws JsonProcessingException {
        String jsonStr = getString(json, propertyName);
        return jsonToList(jsonStr, clazz);
    }

    /**
     * 通用的反序列化方法，将JSON字符串转换为对象，支持多层泛型嵌套<br>
     * 例如要将json字符串解析为{@code List<Set<String>>}类型，调用方式如下<br>
     * {@code List<Set<String>> list = jsonToObject(json, List.class, Set.class, String.class);}<br>
     * 嵌套的每层只支持一种泛型，不支持单层多泛型，例如{@code List<Map<String, Long>>}这种不受支持
     *
     * @param json    JSON 字符串
     * @param classes 嵌套的类型，按从外到内的顺序
     * @param <E>     最外层类型
     * @return 反序列化后的对象
     * @throws Exception 反序列化异常
     */
    public static <E> E jsonToObject(String json, Class<?>... classes) throws Exception {
        TypeFactory typeFactory = MAPPER.getTypeFactory();
        JavaType javaType = buildJavaType(typeFactory, classes);
        return MAPPER.readValue(json, javaType);
    }

    /**
     * 递归构建 JavaType
     *
     * @param typeFactory TypeFactory 实例
     * @param classes     嵌套的类型，按从外到内的顺序
     * @return 构建好的 JavaType
     */
    private static JavaType buildJavaType(TypeFactory typeFactory, Class<?>... classes) {
        if (classes == null || classes.length == 0) {
            throw new IllegalArgumentException("至少需要一个classes参数");
        } else if (classes.length == 1) {
            // 如果没有内层类，直接返回当前类的 JavaType
            return typeFactory.constructType(classes[0]);
        } else {
            // 递归构建内层类的 JavaType
            JavaType innerType = buildJavaType(typeFactory, Arrays.copyOfRange(classes, 1, classes.length));
            // 构建当前层的 JavaType
            return typeFactory.constructParametricType(classes[0], innerType);
        }
    }
}
